// kernel.cc 
//	Initialization and cleanup routines for the Nachos kernel.
//
// Copyright (c) 1992-1996 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "debug.h"
#include "main.h"
#include "kernel.h"
#include "sysdep.h"
#include "synch.h"
#include "synchlist.h"
#include "libtest.h"
#include "elevatortest.h"
#include "string.h"

//----------------------------------------------------------------------
// ThreadedKernel::ThreadedKernel
// 	Interpret command line arguments in order to determine flags 
//	for the initialization (see also comments in main.cc)  
//----------------------------------------------------------------------

ThreadedKernel::ThreadedKernel(int argc, char **argv)
{
   randomSlice = FALSE; 
    test = 0;
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-rs") == 0) {
 	    ASSERT(i + 1 < argc);
	    RandomInit(atoi(argv[i + 1]));// initialize pseudo-random
					// number generator
	    randomSlice = TRUE;
	    i++;
        } else if (strcmp(argv[i], "-u") == 0) {
            cout << "Partial usage: nachos [-rs randomSeed] [-t (locks|mailbox|join|alarm|scheduler)]\n";
	} else if (strcmp(argv[i], "-t") == 0) {
	    ASSERT(i + 1 < argc);
	    i++;
	    if(strcmp(argv[i], "locks") == 0) {
	    	test = 1;
	    } else if(strcmp(argv[i], "mailbox") == 0) {
	    	test = 2;
	    } else if(strcmp(argv[i], "join") == 0) {
	    	test = 3;
	    } else if(strcmp(argv[i], "alarm") == 0) {
	    	test = 4;
	    } else if(strcmp(argv[i], "scheduler") == 0) {
	    	test = 5;
	    } else {
	    	ASSERT(false);
	    }
	}
    }
}

//----------------------------------------------------------------------
// ThreadedKernel::Initialize
// 	Initialize Nachos global data structures.  Separate from the 
//	constructor because some of these refer to earlier initialized
//	data via the "kernel" global variable.
//----------------------------------------------------------------------

void
ThreadedKernel::Initialize()
{
    stats = new Statistics();		// collect statistics
    interrupt = new Interrupt;		// start up interrupt handling
    scheduler = new Scheduler();	// initialize the ready queue
    alarm = new Alarm(randomSlice);	// start up time slicing

    // We didn't explicitly allocate the current thread we are running in.
    // But if it ever tries to give up the CPU, we better have a Thread
    // object to save its state. 
    currentThread = new Thread("main");		
    currentThread->setStatus(RUNNING);

    interrupt->Enable();
}

//----------------------------------------------------------------------
// ThreadedKernel::~ThreadedKernel
// 	Nachos is halting.  De-allocate global data structures.
//----------------------------------------------------------------------

ThreadedKernel::~ThreadedKernel()
{
    delete alarm;
    delete scheduler;
    delete interrupt;
    delete stats;
    
    Exit(0);
}

//----------------------------------------------------------------------
// ThreadedKernel::Run
// 	Run the Nachos kernel.  For now, do nothing. In practice,
//	after initializing data structures, the kernel would
//	start a user program to print the login prompt.
//----------------------------------------------------------------------

void
ThreadedKernel::Run()
{
    // NOTE: if the procedure "main" returns, then the program "nachos"
    // will exit (as any other normal program would).  But there may be
    // other threads on the ready list (started in SelfTest).  
    // We switch to those threads by saying that the "main" thread 
    // is finished, preventing it from returning.
    currentThread->Finish();
    // not reached
}

//----------------------------------------------------------------------
// ThreadedKernel::SelfTest
//      Test whether this module is working.
//----------------------------------------------------------------------

//-----------------MailBox Test -----------------------------
MailBox *mail;
void 
MailBoxTest_send()
{
     int i = 3;
       cout<<"Sending...."<<"value"<<i<<"\n";
       mail->Send(i);
       cout<<"Sent ... "<<"value"<<i<<"\n";    
}

void 
MailBoxTest_receive()
{   int mess_value = 0;
       cout<<"Receiveing/waiting....\n";
       mail->Receive(&mess_value);
       cout<<"Received ... "<<"value"<<mess_value<<"\n";
}

void 
MailBoxTest()
{
     Thread *sendThread,*receiveThread;
     mail = new MailBox();
     sendThread = new Thread("Send_Thread");
     sendThread->Fork( (VoidFunctionPtr)MailBoxTest_send, NULL);
     receiveThread = new Thread("Reive_Thread");
     receiveThread->Fork( (VoidFunctionPtr)MailBoxTest_receive, NULL);

}

//----------------------MailBox Test--------------//






//---------------------------------------
// Test for Alarm::WaitUntil
// thread function
//---------------------------------------
void AlarmTest(Alarm* alarm)
{
	int i = 1;
	while(i <= 10)
	{
		cout << "[" << kernel->stats->totalTicks << "]" << " Thread " << kernel->currentThread->getName() << ": value of i: " << i << endl;
		i++;
		alarm->WaitUntil(100);
	}
	DEBUG(dbgThread, "Ending thread function for thread " << kernel->currentThread->getName());	
}

// Priority Self Test -----------------------------------------------------------
void
ThreadFunction(void *p)
{
    kernel->currentThread->Yield();
    for (int i = 0; i < 5; i++)
    {
        cout << "<prioriti>: Thread: " << kernel->currentThread->getName() << ", priority: " << kernel->currentThread->GetPriority() << ", i: " << i << "\n";
        kernel->currentThread->Yield();
    }
}

void
PrioritySelfTest()
{
    Thread *t1, *t2, *t3;

    t1 = new Thread("t1");
    t2 = new Thread("t2");
    t3 = new Thread("t3", PRIORITY_LOW);

    t1->Fork( (VoidFunctionPtr)ThreadFunction, NULL);
    t2->Fork( (VoidFunctionPtr)ThreadFunction, NULL);
    t3->Fork( (VoidFunctionPtr)ThreadFunction, NULL);
}

// End of Priority Self Test -------------------------------------------------------


// Locks, Condition Locks Self Test ------------------------------------------------
Lock* lock;
List<int *> *list;
Condition *condFull, *condEmpty;
int k;

void
Producer(void* p)
{
    int i = 0;
    while (i < 30)
    {
        lock->Acquire();

        if (k == 10)
            condFull->Wait(lock);

        int *p = new int;

        *p = RandomNumber();

        cout << "Thread " << kernel->currentThread->getName() << " is producing " << *p << "\n";
        list->Append(p);
        k++;

        condEmpty->Signal(lock);

        lock->Release();
        i++;
    }
}

void
Consumer(void* c)
{
    int i = 0;
    while (i < 30)
    {
        lock->Acquire();
        if (k == 0) 
            condEmpty->Wait(lock);

        int *p = list->RemoveFront();

        cout << "Thread " << kernel->currentThread->getName() << " is consuming " << *p << "\n";
        k--;

        condFull->Signal(lock);

        lock->Release();
        i++;
    }
}

void
LocksSelfTest()
{
   Thread *producerThread, *consumerThread;

   lock = new Lock("Producer Consumer Lock");

   list = new List<int *>;

   condFull = new Condition("Cond full");
   condEmpty = new Condition("Cond empty");

   k = 0;
   
   producerThread = new Thread("Producer thread");
   producerThread->Fork( (VoidFunctionPtr)Producer, NULL);
   consumerThread = new Thread("Consumer thread");
   consumerThread->Fork( (VoidFunctionPtr)Consumer, NULL);
}

// End of Locks Self Test ----------------------------------------------------------

// Priority Inversion Self Test ----------------------------------------------------

Lock *plock, *plock0;

void
ThreadFunction1(void *p)
{
    plock->Acquire();
    kernel->currentThread->Yield();
    for (int i = 0; i < 5; i++)
    {
        cout << "Thread: " << kernel->currentThread->getName() << ", priority: " << kernel->currentThread->GetPriority() << ", i: " << i << "\n";
        kernel->currentThread->Yield();
    }
    plock->Release();
}

void
ThreadFunction2(void *p)
{
    plock0->Acquire();
    kernel->currentThread->Yield();
    for (int i = 0; i < 5; i++)
    {
        cout << "Thread: " << kernel->currentThread->getName() << ", priority: " << kernel->currentThread->GetPriority() << ", i: " << i << "\n";
        kernel->currentThread->Yield();
    }
    plock0->Release();
}

int z;

void
ThreadFunction0(void *p)
{
    plock->Acquire();
    plock0->Acquire();

    if (z == 0) {
        Thread *t2 = new Thread("t2", PRIORITY_MED);
        t2->Fork( (VoidFunctionPtr)ThreadFunction1, NULL);
        Thread *t3 = new Thread("t3", PRIORITY_HIGH);
        t3->Fork( (VoidFunctionPtr)ThreadFunction2, NULL);
        z = 1;
    }


    for (int i = 0; i < 5; i++)
    {
        cout << "Thread: " << kernel->currentThread->getName() << ", priority: " << kernel->currentThread->GetPriority() << ", i: " << i << "\n";
        kernel->currentThread->Yield();
    }



    plock->Release();
    cout << "Thread: " << kernel->currentThread->getName() << ", priority: " << kernel->currentThread->GetPriority() <<  " am facut release la primul lock\n";

    plock0->Release();


    cout << "Thread: " << kernel->currentThread->getName() << ", priority: " << kernel->currentThread->GetPriority() <<  " am facut release la al doilea lock\n";
}




void
InversionSelfTest()
{
    Thread *t1;

    plock = new Lock("xxx Lock");
    plock0 = new Lock("yyy Lock");

    z = 0;
    t1 = new Thread("t1", PRIORITY_LOW);

    t1->Fork( (VoidFunctionPtr)ThreadFunction0, NULL);
}

// End Priority Inversion Self Test -------------------------------------------------


Thread *tf1, *tf2;

void
ThreadFunction5(void *p)
{

    cout << "Thread: " << kernel->currentThread->getName() << " waiting for tf2\n";
    tf1->Join(tf2);

    for (int i = 0; i < 5; i++)
    {
        cout << "Thread: " << kernel->currentThread->getName() << ", i: " << i << "\n";
        kernel->currentThread->Yield();
    }

}

void
ThreadFunction6(void *p)
{

    for (int i = 0; i < 5; i++)
    {
        cout << "Thread: " << kernel->currentThread->getName() << ", i: " << i << "\n";
        kernel->currentThread->Yield();
    }

}

void JoinSelfTest()
{
    tf2 = new Thread("tf2", TRUE);
    tf1 = new Thread("tf1", PRIORITY_HIGH);

    tf1->Fork ((VoidFunctionPtr)ThreadFunction5, NULL);
    tf2->Fork ((VoidFunctionPtr)ThreadFunction6, NULL);
    
}

void AlarmSelfTest(Alarm* alarm)
{
    //-----------------------
   // Test waituntil
   //-----------------------
   Thread *waitThread = new Thread("[cyrus]");
   Thread *waitThread1 = new Thread("[cyrus1]");
   waitThread->Fork((VoidFunctionPtr)AlarmTest, (void*) alarm);
   waitThread1->Fork((VoidFunctionPtr)AlarmTest, (void*) alarm);
}


void
ThreadedKernel::SelfTest() {
   Semaphore *semaphore;
   SynchList<int> *synchList;
   
   LibSelfTest();		// test library routines
   
   /* semaphore = new Semaphore("test", 0);
   semaphore->SelfTest();
   delete semaphore;
   
   				// test locks, condition variables
				// using synchronized lists
   synchList = new SynchList<int>;
   synchList->SelfTest(9);
   delete synchList;*/

   switch(test) {
   	case 1:
   	    LocksSelfTest();
   	    break;
   	case 2:
   	    MailBoxTest();
   	    break;
   	case 3:
   	    JoinSelfTest();
   	    break;
   	case 4:
   	    AlarmSelfTest(alarm);
   	    currentThread->SelfTest();
   	    semaphore = new Semaphore("test", 0);
	    semaphore->SelfTest();
	    delete semaphore;
	    synchList = new SynchList<int>;
   	    synchList->SelfTest(9);
            delete synchList;
   	    break;
   	case 5:
   	    PrioritySelfTest();
   	    InversionSelfTest();
   	    break;
   	default:
   	    currentThread->SelfTest();
   	    break;
   }
   
   //LocksSelfTest();
   //MailBoxTest();
   //PrioritySelfTest();
   //ElevatorSelfTest();

   //JoinSelfTest();
}
